package com.kaixinff.name;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.kaixinff.name.User.Gender;

public class NameUtil {
	
	public static void main(String[] args) throws Exception {
		int num = 100;
		Gender gender = Gender.MALE;
		List<User> users = genNames(num, gender);
		saveUsers("resources/result.txt", users);
	}
	
	public static List<User> loadUsers(String filename) throws IOException {
		File f = new File(filename);
		if (!f.exists()) {
			return new ArrayList<User>();
		} else {
			return loadUsers(f);
		}
	}
	
	public static List<User> loadUsers(URI uri) throws IOException {
		File f = new File(uri);
		if (!f.exists()) {
			return new ArrayList<User>();
		} else {
			return loadUsers(f);
		}
	}
	
	public static List<User> loadUsers(File f) throws IOException {
		List<User> users = new ArrayList<User>();
		FileInputStream fin = null;
		ByteArrayOutputStream out = null;
		try {
			fin = new FileInputStream(f);
			out = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = fin.read(buffer)) > 0) {
				out.write(buffer, 0, len);
			}
			String json = new String(out.toByteArray(), "UTF-8");
			if (json.length() > 0) {
				JSONArray jarray = new JSONArray(json);
				for (int i = 0, n = jarray.length(); i < n; i++) {
					JSONObject jobj = jarray.getJSONObject(i);
					users.add(User.toUser(jobj));
				}
			}
		} catch (JSONException e) {
			throw new IOException(e);
		} finally {
			if (fin != null) {
				fin.close();
			}
			if (out != null) {
				out.close();
			}
		}
		return users;
	}
	
	public static void saveUsers(String filename, List<User> users) throws IOException {
		File f = new File(filename);
		if (f.exists()) {
			saveUsers(f, users);
		}
	}
	
	public static void saveUsers(URI uri, List<User> users) throws IOException {
		File f = new File(uri);
		if (f.exists()) {
			saveUsers(f, users);
		}
	}
	
	public static void saveUsers(File f, List<User> users) throws IOException {
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(f);
			out.write('[');
			boolean isFirst = true;
			for (User user: users) {
				if (isFirst) {
					isFirst = false;
				} else {
					out.write(',');
					out.write('\n');
				}
				String result = user.toJSON().toString();
				byte[] bytes = result.getBytes("UTF-8");
				out.write(bytes);
			}
			out.write(']');
		} catch (JSONException e) {
			throw new IOException(e);
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}
	
	public static List<User> genNames(int num, Gender gender) throws IOException{
		List<User> result = new ArrayList<User>();
		Set<String> usernames = new HashSet<String>();
		List<String> familyNames = getContent("family-name-cn.txt");
		List<String> enNames = getContent("name-" + gender + "-en.txt");
		List<String> cnNames = getContent("name-" + gender + "-cn.txt");
		for (int i = 0; i < num; i++) {
			int index = new Random().nextInt(cnNames.size());
			String cnName = cnNames.get(index);
			cnNames.remove(index);
			
			String enName = null; 
			if (new Random().nextBoolean()) {
				index = new Random().nextInt(enNames.size());
				enName = enNames.get(index);
				enNames.remove(index);
			} else {
				enName = toHanyuPinyinString(cnName);
			}
			
			index = new Random().nextInt(familyNames.size());
			String familyName = familyNames.get(index);
			int y = 1980 + new Random().nextInt(10);
			int m = new Random().nextInt(12) + 1;
			int d = new Random().nextInt(28) + 1;
			String username = enName + toHanyuPinyinString(familyName);
			String name = familyName + cnName;
			if (usernames.contains(username)) {
				username = username + y;
			}
			if (usernames.contains(username) || username.length() < 6 ||  username.length() > 18) {
				i--;
			} else {
				usernames.add(username);
			}
			User user = new User();
			user.setName(name);
			user.setId(username);
			user.setGender(gender.getValue());
			user.setYear(y);
			user.setMonth(m);
			user.setDay(d);
			result.add(user);
		}
		return result;
	}
	
	private static List<String> getContent(String name) throws IOException {
		List<String> results = new ArrayList<String>();
		InputStream in = NameUtil.class.getClassLoader().getResourceAsStream("resources/" + name);
		if (in == null) {
			in = new FileInputStream("resources/" + name);
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		byte[] b = new byte[1];
		int len = 0;
		while (len > -1) {
			len = in.read(b);
			if (b[0] == '\r' || b[0] == '\n' || len == -1) {
				String result = new String(out.toByteArray(), "UTF-8");
				out = new ByteArrayOutputStream();
				if (result.length() > 0) {
					results.add(result);
				}
			} else {
				out.write(b);
			}
		}
		return results;
	}

	private static String toHanyuPinyinString(String text) {
		HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
		format.setCaseType(HanyuPinyinCaseType.LOWERCASE); 
		format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		format.setVCharType(HanyuPinyinVCharType.WITH_V);
		StringBuffer sb = new StringBuffer();
		for (int i = 0, n = text.length(); i < n; i++) {
			char ch = text.charAt(i);
			try {
				String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
				if (pinyinArray != null && pinyinArray.length > 0) {
					sb.append(pinyinArray[0]);
				} else {
					sb.append(ch);
				}
			} catch (BadHanyuPinyinOutputFormatCombination e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}
}
